package eu.vranckaert.calendar;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import eu.vranckaert.calendar.util.*;
import eu.vranckaert.calendar.widget.BlocksLayout;
import eu.vranckaert.calendar.widget.ObservableScrollView;
import eu.vranckaert.calendar.widget.Workspace;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * User: Dirk Vranckaert
 * Date: 20/02/13
 * Time: 15:32
 */
@SuppressLint({ "UseSparseArrays", "SimpleDateFormat" })
public class CalendarWeekView extends CalendarView {
    private WeekDay firstDayOfWeek;

    private CalendarWeekView(Activity context, Workspace workspace, int blockColorResId) {
        super(context, workspace, blockColorResId);
    }

    public static CalendarWeekView initCalendar(Activity context, Workspace workspace, Date dayInWeek, int blockColorResId, WeekDay firstDayOfWeek, OnDataLoadListener onDataLoadListener) {
        CalendarWeekView helper = new CalendarWeekView(context, workspace, blockColorResId);
        helper.setOnDataLoadListener(onDataLoadListener);
        helper.firstDayOfWeek = firstDayOfWeek;
        helper.setupFirstFiveViews(dayInWeek);
        helper.updateNowView(true);

        return helper;
    }

    public static CalendarWeekView initCalendar(Activity context, Workspace workspace, Date dayInWeek, int blockColorResId, List<Integer> blockColorResIds, WeekDay firstDayOfWeek, OnDataLoadListener onDataLoadListener) {
        CalendarWeekView helper = new CalendarWeekView(context, workspace, blockColorResId);

        helper.setBlockColorResIds(blockColorResIds);
        helper.setOnDataLoadListener(onDataLoadListener);
        helper.firstDayOfWeek = firstDayOfWeek;
        helper.setupFirstFiveViews(dayInWeek);
        helper.updateNowView(true);

        return helper;
    }

    /**
     * A helper class containing object references related to a particular firstDayOfWeek in the schedule.
     */
    private class Week extends Data {
        private Date firstDayOfWeek;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Week week1 = (Week) o;

            if (firstDayOfWeek != null ? !firstDayOfWeek.equals(week1.firstDayOfWeek) : week1.firstDayOfWeek != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return firstDayOfWeek != null ? firstDayOfWeek.hashCode() : 0;
        }
    }

    @Override
    protected void setupFirstFiveViews(Date date) {
        Date firstDayOfWeekTwoWeeksAgo = DateUtil.DateTimeConverter.calculateWeekBoundaries(-2, date, firstDayOfWeek.getWeekday(), context).get(0);
        Date firstDayOfWeekOneWeekAgo = DateUtil.DateTimeConverter.calculateWeekBoundaries(-1, date, firstDayOfWeek.getWeekday(), context).get(0);
        Date firstDayOfThisWeek = DateUtil.DateTimeConverter.calculateWeekBoundaries(0, date, firstDayOfWeek.getWeekday(), context).get(0);
        Date firstDayOfWeekNextWeek = DateUtil.DateTimeConverter.calculateWeekBoundaries(1, date, firstDayOfWeek.getWeekday(), context).get(0);
        Date firstDayOfWeekInTwoWeeks = DateUtil.DateTimeConverter.calculateWeekBoundaries(2, date, firstDayOfWeek.getWeekday(), context).get(0);

        setupView(firstDayOfWeekTwoWeeksAgo, false);
        setupView(firstDayOfWeekOneWeekAgo, false);
        setupView(firstDayOfThisWeek, false);
        setupView(firstDayOfWeekNextWeek, false);
        setupView(firstDayOfWeekInTwoWeeks, false);
    }

    private Week setupView(Date firstDayOfWeek, boolean addToBeginning) {
        Week week = new Week();

        // Setup views
        week.setRootView((ViewGroup) context.getLayoutInflater().inflate(R.layout.cal__blocks_content, null));

        week.setScrollView((ObservableScrollView) week.getRootView().findViewById(R.id.blocks_scroll));
        week.getScrollView().setOnScrollListener(this);

        week.setBlocksView((BlocksLayout) week.getRootView().findViewById(R.id.blocks));
        week.getBlocksView().setColumns(7);
        week.setNowView(week.getRootView().findViewById(R.id.blocks_now));

        week.getBlocksView().setDrawingCacheEnabled(true);
        week.getBlocksView().setAlwaysDrawnWithCacheEnabled(true);

        firstDayOfWeek = DateUtil.DateTimeConverter.convertDateTimeToDateOnly(firstDayOfWeek);
        week.firstDayOfWeek = firstDayOfWeek;

        if (addToBeginning) {
            workspace.addView(week.getRootView(), 0);
            mData.add(0, week);
        } else {
            workspace.addView(week.getRootView());
            mData.add(week);
        }

        setHeader(week);

        reloadDataForDay(week.firstDayOfWeek);

        return week;
    }

    private void setHeader(Week week) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(week.firstDayOfWeek);

        Date day1 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day2 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day3 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day4 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day5 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day6 = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date day7 = calendar.getTime();

        View header = week.getRootView().findViewById(R.id.calendar_week_header);

        TextView label1 = (TextView) header.findViewById(R.id.calendar_week_header_1);
        TextView label2 = (TextView) header.findViewById(R.id.calendar_week_header_2);
        TextView label3 = (TextView) header.findViewById(R.id.calendar_week_header_3);
        TextView label4 = (TextView) header.findViewById(R.id.calendar_week_header_4);
        TextView label5 = (TextView) header.findViewById(R.id.calendar_week_header_5);
        TextView label6 = (TextView) header.findViewById(R.id.calendar_week_header_6);
        TextView label7 = (TextView) header.findViewById(R.id.calendar_week_header_7);

        label1.setText(getHeaderDay(day1) + System.getProperty("line.separator")+ getHeaderDayMonth(day1));
        label2.setText(getHeaderDay(day2) + System.getProperty("line.separator")+ getHeaderDayMonth(day2));
        label3.setText(getHeaderDay(day3) + System.getProperty("line.separator")+ getHeaderDayMonth(day3));
        label4.setText(getHeaderDay(day4) + System.getProperty("line.separator")+ getHeaderDayMonth(day4));
        label5.setText(getHeaderDay(day5) + System.getProperty("line.separator")+ getHeaderDayMonth(day5));
        label6.setText(getHeaderDay(day6) + System.getProperty("line.separator")+ getHeaderDayMonth(day6));
        label7.setText(getHeaderDay(day7) + System.getProperty("line.separator")+ getHeaderDayMonth(day7));


        header.setVisibility(View.VISIBLE);
        header.invalidate();
        header.forceLayout();
    }

    private String getHeaderDay(Date date) {
        DateFormat df = new SimpleDateFormat("E");
        return df.format(date);
    }

    private String getHeaderDayMonth(Date date) {
        DateFormat df = new SimpleDateFormat("d/MM");
        return df.format(date);
    }

    @Override
    protected boolean updateNowView(boolean forceScroll) {
        Week nowWeek = null; // effectively Day corresponding to today
        for (Data data : mData) {
            Week week = (Week) data;
            if (DateUtil.DateTimeConverter.isDayThisWeek(week.firstDayOfWeek, firstDayOfWeek.getWeekday(), context)) {
                nowWeek = week;
                week.getNowView().setVisibility(View.VISIBLE);
            } else {
                week.getNowView().setVisibility(View.GONE);
            }
        }

        if (nowWeek != null && forceScroll) {
            // Scroll to show "now" in center
            workspace.setCurrentScreenFast(CURRENT_INDEX);
            drawNowTime(nowWeek);
            return true;
        }

        return false;
    }

    private void drawNowTime(Week week) {
        final int offset = week.getScrollView().getHeight() / 2;
        week.getNowView().requestRectangleOnScreen(new Rect(0, offset, 0, offset), true);
        week.getBlocksView().requestLayout();
    }

    @Override
    public void onViewChanged(int index) {
        if (index != CURRENT_INDEX) {
            Week newWeek = null;
            if (CURRENT_INDEX < index) {
                // Move forward
                Calendar cal = Calendar.getInstance();
                cal.setTime(((Week)mData.get(CURRENT_INDEX)).firstDayOfWeek);
                cal.add(Calendar.WEEK_OF_YEAR, 3);

                mData.remove(0);
                workspace.removeViewAt(0);

                newWeek = setupView(cal.getTime(), false);
            } else {
                // Move backwards
                Calendar cal = Calendar.getInstance();
                cal.setTime(((Week)mData.get(CURRENT_INDEX)).firstDayOfWeek);
                cal.add(Calendar.WEEK_OF_YEAR, -3);

                int indexForRemoval = mData.size() - 1;
                mData.remove(indexForRemoval);
                workspace.removeViewAt(indexForRemoval);

                newWeek = setupView(cal.getTime(), true);
            }

            reloadDataForDay(newWeek.firstDayOfWeek);

            updateNowView(false);
            super.onViewChanged(index);
        }
    }

    @Override
    public Date getCurrentDate() {
        return ((Week)mData.get(CURRENT_INDEX)).firstDayOfWeek;
    }

    @Override
    public void reloadDataForDay(Date date) {
        Date firstDayOfWeek = DateUtil.DateTimeConverter.calculateWeekBoundaries(0, date, this.firstDayOfWeek.getWeekday(), context).get(0);

        if (getOnDataLoadListener() != null) {
            Week week = new Week();
            week.firstDayOfWeek = firstDayOfWeek;

            if (mData.contains(week)) {
                week = (Week) mData.get(mData.indexOf(week));
                DataLoadingTask dataLoadingTask = new DataLoadingTask();
                AsyncHelper.startWithParams(dataLoadingTask, new Week[]{week});
            }
        }
    }

    private class DataLoadingTask extends AsyncTask<Week, Void, List<Object>> {
        @Override
        protected List<Object> doInBackground(Week... params) {
            Week week = params[0];

            Map<Integer, List<? extends TimeBlock>> timeBlockMap = new HashMap<Integer, List<? extends TimeBlock>>();
            for (int i = 0; i<7; i++) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(week.firstDayOfWeek);
                calendar.add(Calendar.DAY_OF_WEEK, i);

                List<? extends TimeBlock> timeBlocks = getOnDataLoadListener().loadDataForDay(calendar.getTime());
                timeBlockMap.put(i, timeBlocks);
            }

            List<Object> result = new ArrayList<Object>();
            result.add(week);
            result.add(timeBlockMap);

            return result;
        }

        @SuppressWarnings("unchecked")
		@Override
        protected void onPostExecute(List<Object> result) {
            Week week = (Week) result.get(0);
            Map<Integer, List<? extends TimeBlock>> timeBlockMap = ((Map<Integer, List<? extends TimeBlock>>) result.get(1));

            if (mData.contains(week)) {
                week.getBlocksView().removeAllBlocks();
                for (Map.Entry<Integer, List<? extends TimeBlock>> entry : timeBlockMap.entrySet()) {
                    displayBlockViews(week, entry.getValue(), entry.getKey());
                }
            }
        }
    }

    @Override
    public void reloadAllData() {
        for (Data data : mData) {
            reloadDataForDay(((Week)data).firstDayOfWeek);
        }
    }
}
